Beginners Programming
Demos
Poker Game with Liberty Basic
[Editor: Please note - I have taken a little editorial liberty with Gordon's article. To be sure, it is an interesting premiss and worth study.]
If you haven't the slightest idea what to program with LB, maybe this is the push you were looking for. I'll try to get you interested in programming a draw poker game. You can download the full version, but it's better to read on.
I like to program games. Even though I don't play games that very often. Of course I don't mean those games where scores of people have to work at. There are many things to discover or learn while programming, that's what I like to talk about. So here is a tutorial about programming a game.
The last time I started with my tutorial I didn't finished either the game or the tutorial. The game was huge and so the tutorial became huge too. Here is a smaller game. This is more concept and less a finished game. What I plan to do is to create a poker hand of cards. That means I will display five cards. Then the program will evaluate the hand. The program will tell you what is significant in your hand. At last the program advices you which cards to keep.
Screen picture from full version game (when it is eventually done)

[Editor: Since the game does not play the other hand, or the betting and play component, maybe this will serve as Gordon's challenge for the reader. He has created a good platform to extend from.]
Let's play poker. Draw (or bluff) poker that is what I am talking about.
Getting Ready:
Get the QCARD32.DLL by Stephen Murphy from website:
http://www.telusplanet.net/public/stevem/
Study the card demo programs by Alyce (a former open source project) at: http://alycesrestaurant.com/
Here is the plan A:
I learned the rank order from the poker game at http://www.rahsoftwareuk.co.uk/
nomainwin
WindowWidth=DisplayWidth
WindowHeight=DisplayHeight
UpperLeftX =1 : UpperLeftY=1
dim R(52),card(52),suit$(52),Gcard(52,2),suit(13),A(10)
st$(1) = "clubs" :st$(2) = "diamonds" : st$(3) = "hearts" :st$(4) = "spades"
'-----------------------------------------------------------------------------------------------
'JPEG files are loaded with jpeg.dll (Alyce)
'So you need this DLL as well if you want to use the code below
'Setup and load JPG files
Open "jpeg.dll" for DLL as #j
calldll #j, "LoadImageFile",_
hWnd as ulong,_
"pokb4.jpg" as ptr,_
pokerbord as ulong
Close #j
loadbmp "pokerbord",pokerbord
'-------------------------------------------------------------------------------------------------
Above is the usual stuff for working with JPEG files. I use the JPEG.DLL from Alyce to load a JPEG picture as the background for the game. JPEG images are much smaller then Bitmap images. But if you want you may skip the code above and use the native LB drawing commands.
[Editor: This will load the basic game window shown below]

This is the graphic window we are going to use for displaying the cards. By using the WindowWidth and Height parameters we will create a full screen window.
graphicbox #1.g, 0, 0, WindowWidth,WindowHeight
Open "Card Game" for window_nf as #1
#1 "trapclose [quit]"
These four lines are an alternative if you don't use the pokb4.jpg picture.
'********************************
#1.g "down; fill darkgreen"
#1.g "getbmp pokerbord 0 0 1024 768"
#1.g "drawbmp pokerbord 0 0 ;flush"
#1.g "background pokerbord"
'********************************
These two lines are needed if you have loaded the pokerbord.
'-----------------------------------------------------------------------------------
#1.g "down" 'pen down to draw background
#1.g "drawbmp pokerbord 0 0 ;flush" 'the background
'------------------------------------------------------------------------------------
hBox=hwnd(#1.g) 'get the window graphicbox handle to display the cards
This last line is needed if you use third party DLL's as we'll do with the QCARD dll. We will tell the dll to display in the graphicbox. So we need this window handle name.
Open "qcard32.dll" for dll as #qc 'open the DLL
Call InitializeDeck hBox 'initialize the deck.
The call to InitalixeDeck is, as the writer of the DLL told us, done first and only one time.
playwave "shuffle.wav",sync 'intro a shuffle. This wave file comes together with
'the QCARD32.DLL zip file.
This is how the cards in the deck are placed in QCARD32.DLL

Note that the first card of each suit is the Ace of that suit.
The sequential number of the cards is 1 to 52, so cards number 1, 14, 27 and 40 are aces
The problem with this order of cards is that the suit starts with the Ace. When we would want to sort the suit, the Ace will be the first (lowest rank) card. Maybe you could come up with a better idea. Suppose the deck had 53 cards.
Joker, 2,3,4,5,…K,A of clubs, 2,3,4,……K,A of spades.

That's where the modulus routines would be handy to have.
Someday I hope to ask Stephen Murphy to reconstruct another DLL with the poker order of cards. Or even better, maybe he can expand the existing QCARD32 DLL with an extra set of cards.
The main part of the tutorial continues here.
Prompt "What is your name? ";yourName$
We will create an administrator mode to do some testing when that is needed.
If you answer the prompt with "admin" then you can select your hand by yourself.
Administrators can get a four of clubs by inserting 4c when asked for a card.
To receive a ten of hearts, the admin should insert 10h. Jack = 11 Queen = 12 and 13 = King.
'--------display five cards----------------------------
for i = 1 to 5
[newCard]
cnum = int(rnd(1)*52)+1 'card number cnum varies between 1 and 52
I wonder why Carl didn't just gave us some kind of RaND(52) function to seed a random number between 1 and 52. Are there volunteers to write that function?
[Editor: The reason that RND works the way it does is bacward support for QB, standard implementation across languages (many languages do it this way) and this is how Carl wanted to do it. The function mentioned would be easy to implement.]
Now here is where we deal with the administrator mode.
if yourName$ ="admin" then
prompt "choose a card (number + [cdhs]) ";cnum$
if cnum$="quit" then [quit]
cardValue=Val(left$(cnum$,len(cnum$)-1))
cardSuit=instr("cdhs",right$(cnum$,1))
cnum = cardValue+(cardSuit-1)*13
end if
Pretty neat, don't you think?
The variable cardValue Val(left$(cnum$,len(cnum$)-1)) will get the value of the first or first and second digit from the input (cnum$). While cardSuit instr("cdhs",right$(cnum$,1)) becomes 1 to 4 if the last character of the input matches on of the characters of the string "cdhs".
Cnum will now change through cnum = cardValue+(cardSuit-1)*13
If you don't want to be the admin, then you should enter a name (or just enter) to let the program deal 5 random cards of its own.
Now here is how we deal with the fact that the same card drawn from the deck can't be drawn again.
if R(cnum) = -1 then
goto [newCard]
else
R(cnum)= -1 :card(i)=cnum
End if
My way is not the formal way to program card drawing. Drawing cards from a deck goes like this. First you have to shuffle the deck.
1. Construct an array of 52 elements. Each element its sequential value
dim n(52)
for x=1 to 52
n(x)=x
next x
2. Now point to a spot in the array and swap that card with the last one in the array.
3. Shorten the array by one and repeat
for x=52 to 1 step -1
y=int(rnd(1)*x)+1
t=n(x) 'swap needs a temporary variable
n(x)=n(y) 'swap
n(y)=t 'swap current with last
next x
4. This is the result
for h = 1 to 52
print n(h);" ";
next h
end
The way I did means that I used two arrays to work with. I used an array for card picking and the second one to hold the administration. As I dimensioned the arrays, all their elements got zeroed. Now I pointed out to a spot in one array and look in the other array if I had pointed out to that spot before. Look how it goes.
1. The biggest disadvantage of my system is that the computer will have to do a lot of random tries to pick up the final cards.
2. This is no big disadvantage when we play a poker game with a maximum of only sixteen cards out of a 52 cards deck to pick. By the way you won't notice any time delay at the speed of your computer.
dim n(52)
dim a(52)
for x = 1 to 52
[a] y = int(rnd(0)*52)+1
if a(y)=-1 then [a]
n(x) = y :a(y) = -1 'mark the chosen spot in the reserve array
next x
for h = 1 to 52
print n(h);" ";
next h
end
We don't have to use the next two variables in our program. The writer of the Qcard32.DLL provides these functions in his DLL:
'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx GetCardColor(nCard)
GetCardStatus(nCard) AND SetCardStatus(nCard, bStatus) GetCardBlocked(nCard) AND AdjustCardBlocked(nCard, bValue) IsCardDisabled(nCard), SetCardDisabled(nCard, bValue) GetCardX(nCard), GetCardY(nCard) SetCardX(nCard, nValue), SetCardY(nCard, nValue) GetUsern(nCard) & SetUsern(nCard, nValue)
As a side step, here is a short listing just to do some testing:
s$(1)="clubs"
s$(2)="diamonds"
s$(3)="hearts"
s$(4)="spades"
for t = 1 to 52
s=int((t-.5)/13)+1
v= t-s*13
print "T ";t;" suit ";s$(s);" value ";v
next t
Getting back to the game...
Gcard(i,1) = int((card(i)-.5)/13)+1 'suit 1-4
Gcard(i,2) = card(i)-int((card(i)-.5)/13)*13 'value 1-13
I programmed my own functions because I'll need to do some sorting. I need a double dimension array to be sorted Gcard(52,2). When you sort a two dimension array, both elements can be sorted, but even better the sorted elements keep their attributes. Please editors help me to explain this. It's like sorting records. The records will be sorted out while they keep their contents.
I didn't invent any of this. The method for finding the suit and value's of the cards, described above is called MODULUS.
From Alyce's Restaurant and Carl's function-library:
Modulus
function Modulus(number, divisor)
Modulus=(number)-(int(number/divisor)*divisor)
end function
The tutorial continues
x=150+(i)*100 :y=50 :gosub [displCard]
next i
'-------------------------------------------------------------------------------
[startGame]
'------ Flush test --------
for N = 1 to 5
for Z = 1 to 4
if Gcard(N,1) = Z then A(Z)=A(Z)+1
next Z
next N
for Z = 1 to 4
if A(Z) = 5 then notice "flush ";st$(Z)
if A(Z) = 4 then
notice "nearly flush in ";st$(Z)
for Z1 = 1 to 4
if A(Z1) = 1 then
dColour =Z1
for Z2 = 1 to 5
if Gcard(Z2,1) = dColour then dCard = Z2
'the colour and Gcard are now found
next Z2
end if
next Z1
notice "drop ";st$(Gcard(dColour,1));Gcard(dCard,2)
end if
next Z
The main action in this routine is if Gcard(N,1) = Z then A(Z)=A(Z)+1
The suit of every card is checked and placed in an array. Array A(1) contains the clubs while the spades are in A(4). So we can just compare the values of the elements of the array. This means if A(Z) = 5 then notice "flush ";st$(Z)
To evaluate the hand we can also look for the array elements with value 4. The chance to get the completing flush suit is (13-4) of the 42 left over cards. Maybe the other player also has a flush in this suit in his hand. That would drop the chances to get the flush to (13-9)/42.
The next step if the find the element that contains the breaking colour. The value of one other element must be 1 as there is an element with value 4.
------Test for straight------
This is how human look for straights.
Human are looking for the lowest card. Now when they have found this card they then search through their hand for the following card value. Doing this reveals if there is a straight in the hand.
Here is how you could find the lowest suit card easily.
sort Gcard(), 1,5,2
lowestValue = Gcard(1,2)
highestValue= Gcard(5,2)
From the Liberty Help-file
Here is the syntax for the sort command:
sort arrayName$(), i, j, [,n]
This sorts the array named arrayName$( starting with element i, and ending with element j. If it is a double dimensioned array then the column parameter tells which nth element to use as a sort key. Each WHOLE row moves with its corresponding key as it moves during the sort.
But things can easily be done too without sorting.
Take the first card and compare it with all the other cards in the hand.
'----- lowest and highest suit card-----
min = Gcard(1,2)
max = Gcard(1,2)
for Z = 2 to 5
if Gcard(Z,2)< min then min = Gcard(Z,2)
if Gcard(Z,2)> max then max = Gcard(Z,2)
next Z
'-----------------------------------------
So now take the min card and compare it with the four cards left. Look if there is a cards min+1 in the hand. If there is a min+1 then search the whole hand for a card with value min+2. Etc.
for Z = 1 to 5
if Gcard(Z,2) = min + 1 then
for Z1 = 1 to 5
if Gcard(Z1,2) = min + 2 then
for Z2 = 1 to 5
if Gcard(Z2,2) = min + 3 then
for Z3 = 1 to 5
if Gcard(Z3,2) = min + 4 then
notice "straight highest card ";max
end if
next Z3
end if
next Z2
end if
next Z1
end if
next Z
for Z = 1 to 5
if Gcard(Z,2) = min + 1 then
for Z1 = 1 to 5
if Gcard(Z1,2) = min + 2 then
for Z2 = 1 to 5
if Gcard(Z2,2) = min + 3 then
notice "almost a straight drop ";max
end if
next Z2
end if
next Z1
end if
next Z
for Z = 1 to 5
if Gcard(Z,2) = max - 1 then
for Z1 = 1 to 5
if Gcard(Z1,2) = max - 2 then
for Z2 = 1 to 5
if Gcard(Z2,2) = max - 3 then
notice "almost straight drop ";min
end if
next Z2
end if
next Z1
end if
next Z
-------------Test for the rest ------------------
There are several approaches towards this matter. I did it my way.
First I examined my hand to find how many aces, twos, threes, fours, fives, etc. it contains. Basically we did this method before while finding the suits.
'----------- find all 13 ranks -----------
for Z = 1 to 5
rank(Gcard(Z,2)) = rank(Gcard(Z,2))+1 'as with suit(x)=suit(x)+1
next Z
This is all there is to it. So if rank(6) = 2 then this means that there are two sixes in my hands.
'----------- four of a kind----------
for Z = 1 to 13
if rank(Z) = 4 then
notice "FOUR of a kind ";Z
for Z1 = 1 to 13
if rank(Z1) = 1 then
notice "drop the";Z1
end if
next Z1
end if
next Z
'----------- full house ------------
for Z = 1 to 13
if rank(Z) = 3 then
for Z1 = 1 to 13
if rank(Z1) = 2 then
fullHouse = 1
notice "FULL HOUSE ";Z
end if
next Z1
end if
next Z
'------------ trio --------------
for Z = 1 to 13
if rank(Z) = 3 then
for Z1 = 1 to 13
if rank(Z1) = 1 then
notice "Three of a kind ";Z :Z1=13
end if
next Z1
end if
next Z
'------------ two pair-------------------
TwoPair = 0
for Z = 1 to 13
if rank(Z) = 2 then
for Z1 = Z+1 to 13
if rank(Z1) = 2 then
TwoPair = 1
notice "Two pair ";Z;" and ";Z1
for Z2 = 1 to 13
if rank(Z2) = 1 then notice "drop ";Z2
next Z2
end if
next Z1
end if
next Z
'------------- one pair -----------------
for Z = 1 to 13
if rank(Z) = 2 and TwoPair=0 and fullHouse = 0 then
for Z1 = Z+1 to 13
if rank(Z1) >1 then
Z1 = 13
end if
next Z1
notice "One pair ";Z
end if
next Z
'------------ high card -----------------
'notice "Highest card ";Gcard(5,2);" ";st$(Gcard(5,1))
notice "Highest card ";max
redim R(52)
redim card(52)
redim suit$(52)
redim Gcard(52,2)
redim rank(13)
redim A(10)
goto [again]
wait
Okay smart guy. I never needed the sorting. I just wanted to explain MODULUS and show some use of that function. Programming the bluff poker game gets complicated with the cards arranged the way they are in the QCARD dll. So I can only use the DrawCard(hwnd, ncard, nx, ny) function that is provided with the dll. Nevertheless I enjoyed exploring it.
We will expand this program to a real poker game. The story continues.

[displCard]
'window handle, card index number, x, y
Call DealCard hBox,cnum,x,y
playwave "card.wav",sync
'pause 100 milliseconds between cards
call Pause 100
RETURN
wait
[quit] close #qc:close #1:end
There is no need to cut and paste this tutorial. I provided a good illustration of the snippets with a small demo. Please load the accompanying ZIP file [Editor: The zip file is included in the files archive which is part of the newsletter download - it is called NLPokerGame.zip] and have a taste of poker.
My next tutorial will cover the AI techniques needed to play bluff poker.